package oboe.controllers.timeline;

import oboe.core.Oboe;
import oboe.core.SigSlot;
import oboe.core.SigSlotCollection;
import oboe.resources.Files;

import flash.Vector;

class TimelineController extends SigSlot
{
private var previousGameTime:Float;
private var gameTime:Float;
private var timeline:Vector<EventTrigger>;
private var lastEvent:Int = -1;
private var timelinePaused:Bool;

public function new()
{
	super();
	this.gameTime = 0;
	this.timelinePaused = false;
}

public function slot_loadTimelineFile( file:String ):Void
{
	var data:Array = Files.getFile( file ) as Array<Dynamic>;
	this.slot_addTimelineEvents( data );
}

public function slot_setGameTime( time:Float ):Void
{
	this.previousGameTime = this.gameTime = time;
}

private function sortTimeline():Void
{
	if( !timeline ) return;
	timeline.sort(function( a:EventTrigger, b:EventTrigger ):Int
	{
	var c:Float = a.time - b.time;
	if( c > 0 ) return 1;
	if( c < 0 ) return -1;
	return 0;
	});
}

public function slot_addTargetedEvent( time:Float, target:SigSlotCollection, signal:String, ... args ):Void
{
	if( !timeline )
	{
	timeline = new Vector();
	}
	var p:Array = args.slice();
	p.unshift( signal );
	timeline.push( new EventTrigger( time, p, target ) );
	sortTimeline();
}

public function slot_addTimelineEvent( time:Float, signal:String, ... args ):Void
{
	if( !timeline )
	{
	timeline = new Vector();
	}
	var p:Array = args.slice();
	p.unshift( signal );
	timeline.push( new EventTrigger( time, p ) );
	sortTimeline();
}

public function slot_addTimelineEvents( data:Array ):Void
{
	data.sortOn("t", Array.NUMERIC);
	if( !timeline )
	{
	timeline = new Vector();
	}
	for each( var event:Object in data )
	{
	var time:Float = event.t;
	var cond:Object;
	var et:EventTrigger;
	cond = null;
	if( "c" in event ) cond = event.c;
	for( var s:String in event )
	{
		if( s == "t" || s == "c" ) continue;
		var param:Array = (event[ s ] as Array).slice();
		param.unshift( s );
		timeline.push( et = new EventTrigger( time, param ) );
		et.condition = cond;
	}
	}
	sortTimeline();
}

public function slot_pauseTimeline():Void
{
	this.timelinePaused = true;
}

public function slot_unpauseTimeline():Void
{
	this.timelinePaused = false;
}

public function slot_onTick( time:Float ):Void
{
	if( timelinePaused ) return;
	
	var et:EventTrigger;
	this.previousGameTime = this.gameTime;
	this.gameTime += time;
	Oboe.flags["GAME_TIME"] = this.gameTime;
	var continue2:Bool; // Flash Compiler work around 
	
	if( this.gameTime > this.previousGameTime )
	{
	while( lastEvent < timeline.length-1 && (et = timeline[ lastEvent+1 ]).time <= this.gameTime )
	{
		if( et.condition )
		{
		continue2 = false;
		for( var c:String in et.condition )
		{
			if( Oboe.flags[c] != et.condition[c] )
			{
			lastEvent++;
			continue2 = true;
			break;
			}
		}
		if( continue2 ) continue;
		}
		if( et.target )
		{
		et.target.message.apply( et, timeline[ lastEvent+1 ].signal );
		}
		else
		{
		this.signalUp.apply( null, timeline[ lastEvent+1 ].signal );
		}
		lastEvent++;
		if( timelinePaused ) return;
	}
	}
	else if( this.gameTime < this.previousGameTime )
	{
	while( lastEvent > 0 && (et = timeline[ lastEvent+1 ]).time >= this.gameTime )
	{
		if( et.condition )
		{
		continue2 = false;
		for( var b:String in et.condition )
		{
			if( Oboe.flags[b] != et.condition[b] )
			{
			lastEvent--;
			continue2 = true;
			break;
			}
		}
		if( continue2 ) continue;
		}
		
		if( et.target )
		{
		et.target.message.apply( et, timeline[ lastEvent+1 ].signal );
		}
		else
		{
		this.signalUp.apply( null, timeline[ lastEvent+1 ].signal );
		}
		lastEvent--;
		if( timelinePaused ) return;
	}
	}
}
}
}
import oboe.core.SigSlotCollection;

class EventTrigger
{
public var time:Float;
public var signal:Array;
public var target:SigSlotCollection;
public var condition:Object;
public function EventTrigger( time:Float, signal:Array, target:SigSlotCollection=null)
{
this.time = time;
this.signal = signal;
this.target = target;
}